home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-02-26 | 29.7 KB | 635 lines | [TEXT/CCL2] |
- ;;; -*- Package: CL-USER -*-
-
- ;;; Fill paragraph (m-Q)
-
- ;;; Version 1/27/93
- ;;; Please report bugs and improvements to Carl Gay (cgay@cs.uoregon.edu).
- ;;; Feel free to do whatever you want with this code.
-
- ;;; Change Log:
- ;;; 1/25/93 Released to unsuspecting users. CGay
- ;;; 1/27/93 Removed use of #. which was blowing out during compile-file.
-
-
- ;;; The code in this file implements emacs-like text filling (a la m-Q)
- ;;;
- ;;; The main commands and their default key bindings are:
- ;;;
- ;;; ED-FILL-PARAGRAPH (m-Q) -
- ;;; Fill the "paragraph" surrounding the cursor. A paragraph is defined as
- ;;; the current Lisp comment, if the cursor is in a Lisp comment, or
- ;;; otherwise an attempt is made to heuristicate the text paragraph
- ;;; surrounding the cursor. See the function paragraph-bounds for more
- ;;; details. With a numeric arg, fills the region. If *fill-justification*
- ;;; is non-NIL this will do justification at the same time.
- ;;;
- ;;; ED-SET-FILL-COLUMN (c-X f) -
- ;;; Set the fill column to the current cursor column. With c-U, set it to
- ;;; *default-fill-column* (below). With a numeric argument, set it to that
- ;;; argument.
- ;;;
- ;;; ED-SET-FILL-PREFIX (c-X .) -
- ;;; Set *fill-prefix* to the text preceding the cursor on the current line.
- ;;; If the cursor is at the beginning of the line, cancel the fill prefix.
- ;;;
- ;;; ED-JUSTIFY-PARAGRAPH (c-X j) -
- ;;; Justify the current paragraph. Still has some major bugs, be ye forewarned.
- ;;;
- ;;; *FILL-COLUMN* -
- ;;; A number specifying the column past which text should not extend.
- ;;;
- ;;; *DEFAULT-FILL-COLUMN* -
- ;;; The value of *fill-column* is restored from this when ed-set-fill-column
- ;;; is invoked with a c-U argument.
- ;;;
- ;;; *FILL-PREFIX* -
- ;;; A string, NIL or a function. The default is NIL. See
- ;;; lisp-comment-fill-prefix for an example of the kind of function
- ;;; required.
- ;;;
- ;;; *FILL-JUSTIFICATION* -
- ;;; Type of text justification to do. NIL (the default, meaning don't
- ;;; justify at all) or :LEFT, :RIGHT, :CENTER or :FULL, with the "obvious"
- ;;; meanings. :full seems to work well. I didn't test the others much, and
- ;;; thought about just deleting them...
- ;;;
- ;;; *AUTO-FILL-ENABLED* -
- ;;; Whether or not to automatically fill at the end of a line during normal
- ;;; typing. t, nil, or :lisp-comments. Mostly tested with :lisp-comments.
- ;;;
- ;;; *FILL-SENTENCE-DELIMITERS* -
- ;;; A list of characters after which two spaces (instead of one) should be
- ;;; inserted. The default is '(#\. #\? #\!). Some people may want to add
- ;;; colon (:) to this list.
- ;;;
- ;;; To do:
- ;;; - Make justify-paragraph undoable.
- ;;; - If an error occurs during fill, restore buffer to original state (Undo)
- ;;; when the user aborts.
- ;;; - Make deletion of empty comment lines, e.g. ";;; <CR>", optional. This
- ;;; could probably be done best by modifying fill-prefix-region-bounds. Default to no
- ;;; deletion.
- ;;; - Make regularizing the spacing between words optional.
- ;;; - Make fill redoable.
- ;;; - Allow filling at other chars besides whitespace (e.g., at hyphens)
- ;;;
- ;;; Bugs:
- ;;; - Justification code is still screwy. :center justification in
- ;;; particular. If invoked on the same paragraph several times in a row the
- ;;; paragraph keeps moving to the right. :-) Low priority. Who's gonna use
- ;;; this anyway?
- ;;; - Doesn't always deal with fonts correctly. (see calls to buffer-insert)
- ;;; - Probably lots of others I can't remember right now.
- ;;; - Some of the global vars should be on a per-buffer basis. e.g., buffer
- ;;; properties. In particular *auto-fill-enabled*.
-
- ;;; Search for +++ to find things that need fixing.
-
-
- (defparameter *default-fill-column* 76)
- (defvar *fill-column* *default-fill-column*)
- (defvar *fill-justification* nil)
- (defvar *fill-prefix* nil)
- (defparameter *auto-fill-enabled* :lisp-comments)
- (defparameter *fill-sentence-delimiters* (list #\. #\? #\!))
- (defparameter *fill-whitespace* (coerce '(#\space #\tab #\page #\linefeed) 'string))
- (defparameter *fill-whitespace&cr* (concatenate 'string *fill-whitespace*
- (string #\Return)))
-
- ;;; Dynamically scoped numeric arg.
- (defvar *numeric-arg* nil)
-
- (defmacro with-numeric-arg ((window &optional allow-control-u) &body body)
- `(let ((*numeric-arg* (slot-value ,window 'ccl::prefix-argument)))
- (when (and (not ,allow-control-u) (consp *numeric-arg*))
- (setq *numeric-arg* (car *numeric-arg*)))
- . ,body))
-
- (defmethod run-fred-command :around ((w fred-mixin) arg)
- (declare (ignore arg))
- (with-numeric-arg (w :allow-control-u)
- (call-next-method)))
-
- ;;; A few fewer chars to type.
- (defmacro bpos (buffer)
- `(buffer-position ,buffer))
-
- ;;; An abbreviation for a common idiom.
- (defun skip-whitespace (buffer start end &optional cr-too from-end)
- (buffer-not-char-pos buffer (if cr-too *fill-whitespace&cr* *fill-whitespace*)
- :start start :end end :from-end from-end))
-
- ;;; This will be right 99.9% of the time.
- (defun in-lisp-comment-p (buffer &optional position)
- (let ((c (skip-whitespace buffer
- (buffer-line-start buffer position)
- (buffer-line-end buffer position))))
- (values (and c (char-equal (buffer-char buffer c) #\;))
- c)))
-
- ;;; Find the bounds of the text surrounding the cursor that begins with the
- ;;; current fill prefix.
- ;;; Probably a better way.
- (defun fill-prefix-region-bounds (buffer)
- (do ((i 0 (+ i 1)) (start) (end) (b) (f))
- ()
- (multiple-value-bind (bol-backward shortfallp) ; Dylan, take me away!
- (buffer-line-start buffer nil (- i))
- (multiple-value-bind (eol-forward longfallp)
- (buffer-line-end buffer nil i)
- (setq b (and (or (zerop i) b)
- (not shortfallp)
- (fill-prefix-exists-p buffer bol-backward)))
- (setq f (and (or (zerop i) f)
- (not longfallp)
- (fill-prefix-exists-p buffer eol-forward)))
- (when (and (null b) (null f))
- (return (values start end)))
- (when b (setq start bol-backward))
- (when f (setq end eol-forward))))))
-
- ;;; Find the bounds of the "paragraph" surrounding the cursor.
- (defun paragraph-bounds (window)
- (let ((buffer (fred-buffer window))
- ;; +++ Internal. May lose in the future.
- (mark (caar (slot-value window 'ccl::mark-ring))))
- (cond (*numeric-arg* ; Fill the region.
- (if (or (null mark)
- (= (bpos mark) (bpos buffer)))
- ;; +++ This should do something better than error.
- (error "Can't fill the region because no region was specified.")
- (values (min (bpos mark) (bpos buffer))
- (max (bpos mark) (bpos buffer)))))
- ;; Fill the region delimited by the current fill prefix.
- (*fill-prefix*
- (fill-prefix-region-bounds buffer))
- (t ; Fill plain text.
- (text-bounds buffer)))))
-
- ;;; Stub. +++ This needs to take the fill-prefix into account.
- (defun text-bounds (buffer)
- (let ((start 0)
- (end (- (buffer-size buffer) 1)))
- ;; Find the closest paragraph separator after the cursor.
- (dolist (separator '#.(list (format nil "~2%")
- (format nil "|#")))
- (let ((pos (buffer-string-pos buffer separator
- :start (bpos buffer))))
- (and pos (setq end (min end pos)))))
- ;; Find the closest paragraph separator before the cursor.
- (dolist (separator '#.(list (format nil "~2%")
- (format nil "#|")))
- (let ((pos (buffer-string-pos buffer separator :start 0
- :end (bpos buffer) :from-end t)))
- (and pos (setq start (max start (+ pos (length separator)))))))
- ;; For now don't try to fill plain-text comments contained within
- ;; a top-level definition.
- (let ((def-start (buffer-string-pos buffer #.(format nil "~%(")
- :end (bpos buffer) :from-end t)))
- (when def-start
- (multiple-value-bind (sexp-start sexp-end)
- (buffer-current-sexp-bounds buffer (+ def-start 1))
- (when (and sexp-start sexp-end
- (<= sexp-start (bpos buffer) sexp-end))
- ;; +++ This shouldn't err.
- (error "Can't fill a top-level definition.")))))
- #+ignore
- (loop for i from start to end do (princ (buffer-char buffer i)))
- (values start end *fill-prefix*)))
-
- ;;; Determine the length of the fill prefix on the line containing POSITION.
- (defun fill-prefix-length (buffer &optional position)
- (operate-on-fill-prefix buffer position :length
- #'(lambda ()
- (if *fill-prefix* (length *fill-prefix*) 0))))
-
- (defun lisp-comment-fill-prefix (buffer position operation &optional ppend)
- (declare (ignore ppend)) ; No longer used.
- (when (null position) (setq position (bpos buffer)))
- (let (
- ;; If we're computing the length of the fill prefix then we need to
- ;; look at the beginning of the line to see what's there. If we're
- ;; trying to skip over the fill prefix then POSITION should already
- ;; be pointing to the beginning of the fill prefix.
- (start (if (member operation '(:exists-p :length))
- (buffer-line-start buffer position)
- position))
- (eol (buffer-line-end buffer position)))
- (ecase operation
- (:insert
- ;; This is schrod. <- (I don't remember why I wrote that.)
- ;; Maybe I can just get the indentation from the previous line???
- (let* ((begin (buffer-string-pos buffer #.(format nil "~%(")
- :end position :from-end t))
- ;; ccl::lisp-indentation apparently returns a position that is
- ;; at the correct indentation column for this line.
- (pos (and begin (ccl::lisp-indentation buffer begin position)))
- (col (and pos (buffer-column buffer pos))))
- (if (and col (> col 0))
- (progn (dotimes (i col)
- (buffer-insert buffer " " position))
- (buffer-insert buffer ";; " (+ position col)))
- (buffer-insert buffer ";;; " position))))
- (:length
- (let ((x (buffer-not-char-pos buffer *fill-whitespace*
- :start start :end eol)))
- (setq x (buffer-not-char-pos buffer ";"
- :start (or x start) :end eol))
- (if (not x) 0 (- x start))))
- (:skip
- ;; Skip the fill prefix. Note that this assumes a fill prefix exists
- ;; on this line.
- (let ((x (buffer-not-char-pos
- buffer ";"
- :start (buffer-not-char-pos buffer *fill-whitespace*
- :start start :end eol)
- :end eol)))
- (if (null x)
- eol
- ;; It ends one space after the semicolons.
- (if (char-equal #\space (buffer-char buffer x))
- (+ x 1)
- x))))
- (:exists-p
- ;; Find out if the current line already contains a fill prefix.
- (let ((pos (skip-whitespace buffer start eol)))
- (and pos (char-equal (buffer-char buffer pos) #\;))))
- )))
-
- (defun whitespace-fill-prefix (buffer position operation &optional ppend)
- (unless ppend
- (setq ppend (buffer-line-end buffer position)))
- (ecase operation
- (:insert)
- (:skip (skip-whitespace buffer position ppend))
- (:length (let ((bol (buffer-line-start buffer position)))
- (- (skip-whitespace buffer bol ppend) bol)))
- (:exists-p (find (buffer-char buffer (buffer-line-start buffer position))
- *fill-whitespace* :test #'char-equal))))
-
- (defun fill-prefix (buffer position)
- (if (in-lisp-comment-p buffer position)
- 'lisp-comment-fill-prefix
- *fill-prefix*))
-
- ;;; Find the position of the beginning of the next word, skipping whitespace
- ;;; and fill prefix. Can return NIL if no next word found.
- (defun find-next-word (buffer-mark start end)
- ;; If we're already in a word, just return START.
- (if (not (find (buffer-char buffer-mark start) *fill-whitespace&cr*
- :test #'char-equal))
- start
- (if (null *fill-prefix*)
- (skip-whitespace buffer-mark start end :cr-too)
- (loop with pos = start do
- (setq pos (skip-whitespace buffer-mark pos end))
- (if (and pos
- (char-equal (buffer-char buffer-mark pos) #\Return))
- ;; Found a #\Return, so skip the fill prefix if any.
- (progn (incf pos)
- ;; Need to deal with the possibility that we could be
- ;; past END here...
- (setq pos (skip-over-fill-prefix buffer-mark pos end))
- (when (or (null pos)
- (not (find (buffer-char buffer-mark pos)
- *fill-whitespace*
- :test #'char-equal)))
- (return pos)))
- ;; Otherwise, we're at the beginning of a word.
- (return pos))))))
-
- ;;; Called with POSITION pointing to the beginning of a line in BUFFER.
- ;;; This must return the position of the character immediately following
- ;;; the fill prefix, or POSITION if it determines that there is no fill
- ;;; prefix starting at POSITION.
- (defun skip-over-fill-prefix (buffer position ppend)
- (operate-on-fill-prefix
- buffer position :skip
- ;; The default behavior for when *fill-prefix* is a string.
- #'(lambda ()
- (if (null *fill-prefix*)
- position
- (let ((prefix-end (+ position (length *fill-prefix*))))
- (if (>= prefix-end (buffer-size buffer))
- nil
- ;; This could use buffer-substring-p if we didn't care about
- ;; alphabetic case.
- (if (loop for i from position
- for j from 0
- while (< j (length *fill-prefix*))
- as c = (char *fill-prefix* j)
- do (unless (char= c (buffer-char buffer i))
- (return nil))
- finally (return t))
- prefix-end
- position)))))
- ppend))
-
- (defun operate-on-fill-prefix (buffer position operation function &rest args)
- (cond ((null *fill-prefix*)
- (apply 'whitespace-fill-prefix buffer position operation args))
- ((stringp *fill-prefix*)
- (funcall function))
- ((or (functionp *fill-prefix*)
- (symbolp *fill-prefix*))
- (apply *fill-prefix* buffer position operation args))))
-
- ;;; Determine whether the line at buffer/position has a fill prefix already.
- ;;; This is only used to decide whether to insert the fill prefix on the
- ;;; first line of the fill area.
- (defun fill-prefix-exists-p (buffer position)
- (operate-on-fill-prefix
- buffer position :exists-p
- #'(lambda ()
- (when *fill-prefix*
- (loop with eol = (buffer-line-end buffer position)
- for j from 0
- while (< j (length *fill-prefix*))
- as char = (char *fill-prefix* j)
- for pos from (buffer-line-start buffer position)
- do (when (or (>= pos eol)
- (not (char= char (buffer-char buffer pos))))
- (return nil))
- finally (return t))))))
-
- (defun insert-fill-prefix (buffer-mark &optional position insert-cr)
- (when insert-cr
- (buffer-insert buffer-mark #\Return position)
- (incf position))
- (operate-on-fill-prefix
- buffer-mark position :insert
- #'(lambda ()
- (when *fill-prefix*
- (buffer-insert buffer-mark *fill-prefix*
- (or position (bpos buffer-mark)))))))
-
- (defmethod ed-set-fill-prefix ((window fred-mixin))
- (let ((b (fred-buffer window)))
- (if (zerop (buffer-column b))
- (progn (setq *fill-prefix* nil)
- (set-mini-buffer window "Fill prefix cancelled"))
- (progn (setq *fill-prefix*
- (buffer-substring b (bpos b) (buffer-line-start b)))
- (set-mini-buffer window "Fill prefix set to ~S." *fill-prefix*)
- (when (> (length *fill-prefix*) *fill-column*)
- (setq *fill-column* (length *fill-prefix*))
- (format (ccl::view-mini-buffer window)
- " (Fill column extended to ~S.)" *fill-column*))
- ))))
-
- ;;; From my Fred file (with the name changed). I use this to set Fred key
- ;;; bindings so I'll know if I'm replacing anything.
- (defun set-command (comtab keystroke function &optional doc replace)
- (let ((old-function (comtab-get-key comtab keystroke)))
- (unless (or replace ; "Just do it, dammit!"
- (null old-function) ; Not bound
- (null function) ; Unsetting a binding
- (eq old-function function)) ; No change
- (cerror "Install the new command binding anyway."
- "About to replace command binding for ~A with ~S.~@
- It is currently bound to ~S."
- (ccl::keystroke-code-string keystroke) function old-function)))
- (comtab-set-key comtab keystroke function doc)
- keystroke)
-
- (set-command *control-x-comtab* #\. 'ed-set-fill-prefix
- "Set the fill prefix to the text between the cursor and the left margin.")
-
- (defmethod ed-set-fill-column ((w fred-mixin))
- (setq *fill-column* (max 1 (if *numeric-arg*
- (if (consp *numeric-arg*)
- *default-fill-column*
- *numeric-arg*)
- (buffer-column (fred-buffer w)))))
- (set-mini-buffer w "Fill column set to ~S." *fill-column*))
-
- (set-command *control-x-comtab* #\f 'ed-set-fill-column)
-
-
- ;;; The basic method here is to move forward a word at a time and if we go
- ;;; past the fill-column then fill, or if we encounter a #\Return before
- ;;; reaching the fill-column then unfill. An alternate method (probably
- ;;; faster) would be to move down a line at a time not checking between each
- ;;; pair of words, but then it couldn't regularize the spacing between words.
- (defmethod ed-fill-paragraph ((window fred-mixin))
- (when (and *fill-column*
- (> *fill-column* 0))
- (let* ((b (fred-buffer window))
- (*fill-prefix* (fill-prefix b (bpos b))))
- (multiple-value-bind (ppstart ppend) (paragraph-bounds window)
- (fill-text window ppstart ppend)))))
-
- (defun fill-text (window ppstart ppend)
- (let* ((b (make-mark (fred-buffer window)
- (buffer-size (fred-buffer window)))))
- (if (or (stringp ppstart)
- (not (and ppstart ppend)))
- ;; Also remember to punt here if fill-column is <= fill-prefix...
- (progn (ed-beep)
- (set-mini-buffer window (or ppstart
- "The cursor is not in fillable text.")))
- ;; The vars in this let* are buffer marks so that they have a chance
- ;; of remaining correct even if functions called by fill-text modify
- ;; the buffer.
- (let* ((ppend (make-mark b ppend))
- (eopw (make-mark b ppstart))
- (bow (make-mark b))
- (eow (make-mark b))
- (original-text (buffer-substring b ppstart ppend)) ; for Undo
- (style-vector (buffer-get-style b ppstart ppend)) ; for Undo
- kludge)
- ;; Maybe insert the fill prefix on the first line of the fill
- ;; region.
- (unless (or (/= 0 (buffer-column b ppstart))
- (fill-prefix-exists-p b ppstart))
- (insert-fill-prefix b ppstart nil))
- (loop until (> (bpos eopw) (bpos ppend))
- as bowpos = (find-next-word b (bpos eopw) (bpos ppend))
- ;; Stop only when the *beginning* of a word is outside the
- ;; fill region. If a word is partially in the fill region we
- ;; should fill it.
- until (or (null bowpos)
- (>= bowpos (bpos ppend)))
- do
- (set-mark bow bowpos)
- (set-mark eow (or (buffer-char-pos b *fill-whitespace&cr*
- :start bow :end ppend)
- (buffer-line-end bow)))
- (cond ((and (> (buffer-column eow) *fill-column*)
- ;; The current word juts out past the fill column.
- (let ((fp-length (fill-prefix-length b (bpos eow))))
- (or (< (+ (- (bpos eow) (bpos bow)) fp-length)
- *fill-column*)
- (not (= (buffer-column bow) fp-length)))))
- ;; The current word juts out past the fill column (and
- ;; it isn't too big, in combination with the
- ;; fill-prefix, to be filled, or it is too big, but it
- ;; isn't on a line by itself).
- (buffer-delete b eopw bow)
- (let ((save (bpos eopw)))
- (insert-fill-prefix eopw (bpos eopw) t)
- ;; Justify the previous line if *fill-justification*.
- (justify-one-line b save))
- )
-
- ;; If this word fits on the previous line then unfill.
- ;; This always deletes the text between the end of one
- ;; line and the first word on the next line because
- ;; there might be extra blankspace at the beginning of
- ;; the line. If that was the case, then a #\Return is
- ;; inserted again (with the fill prefix). Probably not
- ;; the most efficient, but effective.
- ((< (buffer-line-end eopw) (bpos bow))
- (buffer-delete b eopw bow)
- (when (> (bpos eopw) 0) ; i.e., a previous line exists.
- (let ((end-of-sentence?
- (member (buffer-char b (- (bpos eopw) 1))
- *fill-sentence-delimiters*
- :test #'char=)))
- (if (<= (+ (buffer-column eopw) ; column...
- (- (bpos eow) (bpos bow)) ; + word size...
- (if end-of-sentence? 2 1)) ; + blankspace...
- *fill-column*)
- (buffer-insert b (if end-of-sentence? " " " ") eopw)
- (let ((save (bpos eopw)))
- (insert-fill-prefix b (bpos eopw) t)
- (justify-one-line b save))
- ))))
- ;; Regularize spacing between words.
- ((and (> (- (bpos bow) (bpos eopw)) 1)
- (> (bpos eopw) ppstart)
- (or (not (setq kludge ; to avoid recomputing...
- (member (buffer-char b (- (bpos eopw) 1))
- *fill-sentence-delimiters*
- :test #'char=)))
- (> (- (bpos bow) (bpos eopw)) 2)))
- (buffer-delete b eopw bow)
- (buffer-insert eopw (if kludge " " " "))
- ))
- while (< (bpos eow) (buffer-size b))
- do (set-mark eopw (bpos eow))
- ) ;; end main loop
- ;; Setup something to undo the fill. This should save the cursor
- ;; position.
- (setup-undo window
- #'(lambda ()
- (buffer-delete b ppstart (bpos ppend))
- (buffer-insert-with-style b original-text style-vector ppstart)
- (fred-update window)
- ;; Could put Redo code here, but I won't bother yet,
- ;; since in theory the user can just type m-Q again.
- )
- "Undo Fill")
- ))))
-
- (set-command *comtab* '(:meta #\q) 'ed-fill-paragraph)
-
- ;;; Auto-fill Lisp comments. This makes no attempt to auto-fill regular
- ;;; text, since I don't know of a reliable way to determine whether we're in
- ;;; text or Lisp code. For that matter, lines that begin with semicolons
- ;;; aren't necessarily comments either...foo.
- (defmethod ed-self-insert :around ((window fred-window))
- (let ((b (fred-buffer window)))
- (when (and *auto-fill-enabled*
- *fill-column*
- (characterp *current-character*) ; Can this be NIL?
- (member *current-character* '(#\Return #\Space))
- (> (buffer-column b) *fill-column*)
- (or (not (eql *auto-fill-enabled* :lisp-comments))
- (in-lisp-comment-p b))) ; most expensive test last.
- (let ((*fill-prefix* 'lisp-comment-fill-prefix))
- ;; Might want to skip over the fill-prefix at the beginning of the line
- ;; first, if any.
- (fill-text window (buffer-line-start b) (bpos b))))
- (call-next-method window)))
-
- ;;; The line to be justified is assumed to have the current fill prefix at
- ;;; its beginning.
- (defun justify-one-line (buffer &optional (position (bpos buffer))
- &key direction justification-type)
- ;; Remove whitespace from eol. Find bol (after prefix if any). Remove
- ;; whitespace from bol (unless right justifying). Insert the appropriate
- ;; number of space chars.
- (when (or justification-type *fill-justification*)
- (let* ((eol (buffer-line-end buffer position))
- (bol (buffer-line-start buffer position))
- (left-margin (skip-over-fill-prefix buffer bol eol))
- (right-margin (let ((rm (skip-whitespace buffer bol eol nil :from-end)))
- (and rm (+ rm 1))))
- (first-word (skip-whitespace buffer left-margin eol)))
- ;; Justify between left-margin and right-margin.
- (when (and right-margin (< left-margin right-margin)
- first-word ; Is this line blank?
- (< first-word right-margin)
- (< (- (buffer-column buffer right-margin)
- (buffer-column buffer first-word))
- *fill-column*))
- (when (> eol right-margin) ; Remove whitespace from end of line.
- (buffer-delete buffer right-margin eol))
- (setq left-margin (make-mark buffer left-margin))
- (setq right-margin (make-mark buffer right-margin))
- (when (> first-word (bpos left-margin))
- (buffer-delete buffer (bpos left-margin) first-word))
- (let ((n (- *fill-column* (buffer-column right-margin))))
- (case (or justification-type *fill-justification*)
- (:center
- ;; Insert ~half the spaces just after the fill prefix.
- (dotimes (i (floor n 2))
- (buffer-insert left-margin #\Space)))
- (:right
- ;; Just insert all the spaces directly after the fill prefix.
- (dotimes (i n)
- (buffer-insert left-margin #\Space)))
- (:left
- ;; Just remove all spaces from directly after the fill prefix.
- ;; Already done, above.
- )
- (:full
- ;; Move back and forth across the line and insert spaces until
- ;; the line is justified. Most of the time this will probably
- ;; only go one direction before inserting all the necessary
- ;; spaces. First, left justify: Already done, above.
- #+ignore
- (justify-one-line buffer position :direction direction
- :justification-type :left)
- (loop with x = n
- until (zerop x)
- with start = (make-mark left-margin)
- and end = (make-mark right-margin) do
- ;; Find next whitespace.
- (let ((next (buffer-char-pos buffer *fill-whitespace*
- :start (bpos start)
- :end (bpos end)
- :from-end (not direction))))
- (cond ((and (null next) (= x n))
- (return nil))
- ((and next (< (bpos start) next (bpos end)))
- (buffer-insert buffer #\Space next)
- (decf x)
- ;; Move over this whitespace to the next word.
- (let ((pos (if direction
- (skip-whitespace buffer (+ 1 next)
- (bpos end))
- (skip-whitespace buffer start next nil :from-end))))
- (and pos (set-mark (if direction start end) pos))))
- (t
- (setq direction (not direction))
- (set-mark start (bpos left-margin))
- (set-mark end (bpos right-margin)))))))
- ))))))
-
- (defmethod ed-justify-paragraph ((window fred-mixin))
- (multiple-value-bind (ppstart ppend) (paragraph-bounds window)
- (when (and ppstart ppend (< ppstart ppend))
- (loop with bmark = (make-mark (fred-buffer window) ppstart)
- and direction = t
- do (progn (justify-one-line bmark nil :direction direction
- :justification-type (or *fill-justification*
- :FULL))
- (set-mark bmark (buffer-line-start bmark nil 1))
- (setq direction (not direction)))
- until (>= (bpos bmark) ppend)))))
-
- (set-command *control-x-comtab* #\j 'ed-justify-paragraph)
-
-